package nsoft.swing.engine;

import java.awt.Component;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

import javax.swing.JPanel;

import nsoft.swing.annotation.AButton;
import nsoft.swing.annotation.AComponent;
import nsoft.swing.annotation.APanel;
import nsoft.swing.annotation.Type;
import nsoft.swing.layout.BorderLayoutHelper;
import nsoft.swing.layout.FlowLayoutHelper;
import nsoft.swing.layout.LayoutHelper;
import nsoft.util.BeanUtilities;
import nsoft.util.Property;

public final class NPanel extends JPanel {
	/**
	 * serialVersionUID = 1L.
	 */
	private static final long serialVersionUID = 1L;

	// TODO SUPPOTR FOR OBSELUE PROPS LIKE A.B.C.D
	// TODO SUPPOTR FOR COMPONEN LABELS AND EXRLALIZAIO OF LABELS.
	//TODO Write model for data handling.(UiModel)
	private Object[] controllers;

	private static IComponentFactory componentFactory;

	private static Map<String, LayoutHelper> layoutMap = new HashMap<String, LayoutHelper>();

	static {
		componentFactory = new DefaultComponentFactory();
		layoutMap.put("FlowLayout", new FlowLayoutHelper());
		layoutMap.put("BorderLayout", new BorderLayoutHelper());
	}

	/*
	 * public JPanel buildPanel(Object bean) { APanel panelMetadata =
	 * bean.getClass().getAnnotation(APanel.class); return buildPanel(bean,
	 * panelMetadata); }
	 */

	public NPanel(Object bean, Object... controllersA) {
		controllers = controllersA;
		APanel panelMetadata = bean.getClass().getAnnotation(APanel.class);
		buildPanel(this, bean, panelMetadata);
	}

	private void buildPanel(JPanel panel, Object bean, APanel panelMetadata) {
		if (panelMetadata == null) {
			return;
		}
		panel.setName(panelMetadata.name());
		PropertySetter.setProperties(panel, panelMetadata.properties());
		doLayout(panel, bean, panelMetadata);
		// return panel;
	}

	private JPanel buildPanel(Object bean, APanel panelMetadata) {
		JPanel panel = new JPanel();
		if (panelMetadata == null) {
			return panel;
		}
		buildPanel(panel, bean, panelMetadata);
		return panel;
	}

	private void doLayout(JPanel panel, Object bean, APanel panelMetadata) {
		String layoutName = panelMetadata.layout();
		if (layoutName.contains("(")) {
			layoutName = layoutName.substring(0, layoutName.indexOf("("));
		}
		LayoutHelper layoutHelper = layoutMap.get(layoutName);
		panel.setLayout(layoutHelper.getLayoutManager(panelMetadata));
		for (AComponent componentMetadata : panelMetadata.components()) {
			Component component = getComponent(bean, componentMetadata);
			layoutHelper.addComponent(panel, component, componentMetadata
					.layout());
		}
		for (AButton buttonMetadata : panelMetadata.buttons()) {
			Component component = getButton(bean, buttonMetadata);
			layoutHelper
					.addComponent(panel, component, buttonMetadata.layout());
		}
		for (String property : panelMetadata.fileds()) {
			buildComponent(panel, bean, property, layoutHelper);
		}
	}

	private void buildComponent(JPanel panel, Object bean, String property,
			LayoutHelper layoutHelper) {
		APanel panelMetadata = getPanelMetadata(bean, property);
		if (panelMetadata != null) {
			layoutHelper.addComponent(panel, buildPanel(bean, panelMetadata),
					panelMetadata.layoutProperties());
			return;
		}
		AComponent componentMetadata = getComponentMetadata(bean, property);
		if (componentMetadata != null) {
			Component component = getComponent(bean, componentMetadata);
			layoutHelper.addComponent(panel, component, componentMetadata
					.layout());
			return;
		}
		panelMetadata = getPanelMetadataFromProperty(bean, property);
		if (panelMetadata != null) {
			layoutHelper.addComponent(panel, buildPanel(BeanUtilities
					.getPropertyD(bean, property), panelMetadata),
					panelMetadata.layoutProperties());
			return;
		}
	}

	private APanel getPanelMetadataFromProperty(Object bean, String property) {
		try {
			return getFiled(bean, property).getType().getAnnotation(
					APanel.class);
		} catch (Exception e) {
			return null;
		}
	}

	private APanel getPanelMetadata(Object bean, String property) {

		try {
			return getFiled(bean, property).getAnnotation(APanel.class);
		} catch (Exception e) {
			return null;
		}
	}

	private AComponent getComponentMetadata(Object bean, String property) {

		try {
			return getFiled(bean, property).getAnnotation(AComponent.class);
		} catch (Exception e) {
			return null;
		}
	}

	private Component getComponent(Object bean, AComponent componentMetadata) {
		if (Type.PANEL == componentMetadata.type()) {
			// TODO needs to check the annotation for the fiels.
			APanel panelMetadata;
			try {
				panelMetadata = getPanelMetadata(bean, componentMetadata
						.property());
			} catch (Exception e) {
				panelMetadata = null;
			}
			if (panelMetadata == null) {
				return new NPanel(BeanUtilities.getPropertyD(bean,
						componentMetadata.property()));
			} else {
				return buildPanel(BeanUtilities.getPropertyD(bean,
						componentMetadata.property()), panelMetadata);
			}
		}
		//Use UiModel
		return componentFactory.createComponent(new Property(bean,
				componentMetadata.property()), componentMetadata);
	}

	private Component getButton(Object bean, AButton buttonMetadata) {
		Object controller = bean;
		if (controllers != null && controllers.length > 0)
			for (Object object : controllers) {
				if (object.getClass().getName().equalsIgnoreCase(
						buttonMetadata.controller())) {
					controller = object;
					break;
				}
			}
		return componentFactory.createButton((new Property(controller,
				buttonMetadata.action())), buttonMetadata);
	}

	public void addFlowLayoutHelper(String layout, LayoutHelper helper) {
		layoutMap.put(layout, helper);
	}

	private Field getFiled(Object bean, String property) {
		try {
			if (!property.contains("."))
				return bean.getClass().getDeclaredField(property);

			StringTokenizer tokenizer = new StringTokenizer(property, ".");
			Field field = bean.getClass().getDeclaredField(
					tokenizer.nextToken());
			while (tokenizer.hasMoreTokens() && field != null)
				field = bean.getClass().getDeclaredField(tokenizer.nextToken());
			return field;
		} catch (Exception e) {

		}
		return null;
	}

}
